﻿#region Header

/*
Remote views based on linq
By

Pascal Hauser 
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

and

Raphael Gfeller
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

*/

#endregion

#region Usings

using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using InterLinq.Objects;
using Sebarf.Diagnostics.Interfaces;

#endregion

namespace Shared {
	/// <summary>
	/// Description of the class
	/// </summary>
	public class ObjectBasedDataSource : IObjectSource {
		#region Public Methods

		public ObjectBasedDataSource(Dictionary<Type, IList> data) {
			m_data = data;
		}

		#endregion

		#region Private Fields

		private readonly Dictionary<Type, IList> m_data = new Dictionary<Type, IList>();

		#endregion

		private readonly ReaderWriterLock _lock = new ReaderWriterLock();

		#region IObjectSource Members

		public ReaderWriterLock Lock {
			get { return _lock; }
		}

		public IEnumerable<T> GetObjects<T>() {
			if (m_data.ContainsKey(typeof(T))) {
				return (IEnumerable<T>)m_data[typeof(T)];
			}
			foreach (var item in m_data) {
				if (item.Key.IsAssignableFrom(typeof(T))) {
					var newList = (List<T>)Activator.CreateInstance(typeof(List<>).MakeGenericType(typeof(T)));
					foreach (object tmp in m_data[item.Key]) {
						newList.Add((T)tmp);
					}
					m_data.Add(typeof(T), newList);
					return GetObjects<T>();
				}
			}
			Check.AssertException(false, "could not found data for type: " + typeof(T));
			return null;
		}

		public void AddObject<T>(T obj) {
			try {
				Lock.AcquireWriterLock(-1);
				if (!AddObj(typeof(T), obj)) {
					throw new Exception("Object could not be added to backend provider");
				}
			}
			finally {
				Lock.ReleaseWriterLock();
			}
		}

		public void AddObject(Type t, object obj) {
			try {
				Lock.AcquireWriterLock(-1);
				if (!AddObj(t, obj)) {
					throw new Exception("Object could not be added to backend provider");
				}
			}
			finally {
				Lock.ReleaseWriterLock();
			}
		}

		public void DeleteObject(Type t, object obj) {
			try {
				Lock.AcquireWriterLock(-1);
				DeleteObj(t, obj);
			}
			finally {
				Lock.ReleaseWriterLock();
			}
		}

		#endregion

		private bool AddObj(Type t, object o) {
			try {
				Lock.AcquireWriterLock(-1);
				if (t == null) {
					return false;
				}
				if (m_data.ContainsKey(t)) {
					m_data[t].Add(o);
					return true;
				}
				else {
					if (AddObj(t.BaseType, o)) {
						return true;
					}
					else {
						foreach (Type i in t.GetInterfaces()) {
							if (m_data.ContainsKey(i)) {
								m_data[i].Add(o);
								return true;
							}
						}
						foreach (Type i in t.GetInterfaces()) {
							if (AddObj(i, o)) {
								return true;
							}
						}
					}
				}
				return false;
			}
			finally {
				Lock.ReleaseWriterLock();
			}
		}

		private void DeleteObj(Type t, object o) {
			try {
				Lock.AcquireWriterLock(-1);
				if (t == null) {
					return;
				}
				if (m_data.ContainsKey(t)) {
					m_data[t].Remove(o);
					return;
				}
				else {
					if (AddObj(t.BaseType, o)) {
						return;
					}
					else {
						foreach (Type i in t.GetInterfaces()) {
							if (m_data.ContainsKey(i)) {
								m_data[i].Remove(o);
								return;
							}
						}
						foreach (Type i in t.GetInterfaces()) {
							if (AddObj(i, o)) {
								return;
							}
						}
					}
				}
				return;
			}
			finally {
				Lock.ReleaseWriterLock();
			}
		}
	}
}